%{ open Ast
let parse_error s = (* Called by the parser function on error *)
  print_endline s;
  flush stdout


 %}

%token SEMI LPAREN RPAREN LBRACE RBRACE COMMA COLON
%token PLUS MINUS TIMES DIVIDE POW ASSIGN 
%token EQ NEQ LT LEQ GT GEQ
%token AND OR
%token BOOLEAN DOUBLE STRING INT
%token FALSE TRUE
%token ALPHABET LAMBDA RULES 
%token RETURN IF ELSE WHILE 
%token DEF_COMPUTE DEF_DRAW
%token <int> LITERAL
%token <float> FLOAT
%token <string> STRINGLIT
%token <string> ID
%token EOF

%nonassoc NOELSE
%nonassoc ELSE
%right ASSIGN
%left OR
%left AND
%left EQ NEQ
%left LT GT LEQ GEQ
%left PLUS MINUS
%left TIMES DIVIDE 

/*%nonassoc NOELSE
%nonassoc ELSE
%right ASSIGN
%left EQ NEQ
%left LT GT LEQ GEQ
%left PLUS MINUS
%left TIMES DIVIDE
*/
%start program
%type <Ast.program> program


%%

program:
   /* nothing */ { [] }
 | program fdecl { List.rev ($2 :: $1) }
/*| program fdecl { fst $1, ($2 :: snd $1) }*/ 

fdecl:
   DEF_COMPUTE ID LPAREN formals_opt RPAREN LBRACE vdecl_list stmt_list RBRACE
     { { fname = $2;
	 formals = $4;
	 locals = List.rev $7;
	 body = List.rev $8 } }

formals_opt:
    /* nothing */ { [] }
  | formal_list   { List.rev $1 }

formal_list:
    ID                   { [$1] }
  | formal_list COMMA ID { $3 :: $1 }

vdecl_list:
		/* nothing */  { [] }
		| vdecl_list vdecl { $2 :: $1 }
  



datatype:
  	BOOLEAN { StringType }
	| INT { IntType  }
	| DOUBLE { DoubleType }
/*
| DOUBLE ID ASSIGN FLOAT { DblDecl("double", $2, $4) }
	| BOOLEAN ID ASSIGN TRUE { BoolDecl("boolean", $2, "true") }
	| BOOLEAN ID ASSIGN FALSE { BoolDecl("boolean", $2, "false") } 
  
*/


vdecl:
	 datatype ID ASSIGN expr SEMI { VDecl($1, $2, string_of_expr $4) }

		
		
		/*INT ID ASSIGN LITERAL  { {vtype = "int"; vname = $2; vvalue = string_of_int $4 } }*/
	/*| DOUBLE ID ASSIGN FLOAT { {vtype ="double"; vname =$2; vvalue = string_of_float $4}  }
	| BOOLEAN ID ASSIGN TRUE { {vtype ="boolean"; vname =$2; vvalue = "true"}  }
	| BOOLEAN ID ASSIGN FALSE { {vtype ="double"; vname =$2; vvalue = "false"}  } 
  
	*/
/*| INT ID ASSIGN LITERAL SEMI{ IntDecl("int", $2, $4) }*/


stmt_list:
    /* No empty block allowed */{ [] } 
  | stmt_list stmt { $2 :: $1 }

stmt:
    expr SEMI { Expr($1) }
	/*| datatype ID ASSIGN LITERAL SEMI { Decl($1, $2, string_of_int $4) }*/
  | RETURN expr SEMI { Return($2) }
  | LBRACE stmt_list RBRACE { Block(List.rev $2) }
  | IF LPAREN expr RPAREN stmt %prec NOELSE { If($3, $5, Block([])) }
  | IF LPAREN expr RPAREN stmt ELSE stmt    { If($3, $5, $7) }
  | WHILE LPAREN expr RPAREN stmt { While($3, $5) }

/*
stmt:
    expr SEMI { Expr($1) }
	| INT ID ASSIGN LITERAL SEMI { Decl("int", $2, string_of_int $4) }
	| DOUBLE ID ASSIGN FLOAT SEMI { Decl("int", $2, string_of_float $4) }
	| BOOLEAN ID ASSIGN TRUE SEMI { Decl("int", $2, "true") }
	| BOOLEAN ID ASSIGN FALSE SEMI { Decl("int", $2, "false") }
  | RETURN expr SEMI { Return($2) }
  | LBRACE stmt_list RBRACE { Block(List.rev $2) }
  | IF LPAREN expr RPAREN stmt %prec NOELSE { If($3, $5, Block([])) }
  | IF LPAREN expr RPAREN stmt ELSE stmt    { If($3, $5, $7) }
  | WHILE LPAREN expr RPAREN stmt { While($3, $5) }*/

expr_opt:
    /* nothing */ { Noexpr }
  | expr          { $1 }


dataint:
  LITERAL          { Literal($1) }
	
datafloat:
	FLOAT            { Float($1) }

databool:
		TRUE  { Boolean(bool_of_string "true") }
  | FALSE { Boolean(bool_of_string "false") }


arthvalue:
	  dataint { $1 }
	| datafloat { $1 }
	

bool_expr:
	 	TRUE { BVal(True) }
	| FALSE { BVal(False) }
	| arith_expr EQ arith_expr { EExpr($1, BEqual, $3) }
	| arith_expr NEQ arith_expr { EExpr($1, BNeq, $3) }
	| arith_expr GT arith_expr { RExpr($1, BGreater, $3) }
	| arith_expr GEQ arith_expr { RExpr($1, BGeq, $3) }
	| arith_expr LT arith_expr { RExpr($1, BLess, $3) }
	| arith_expr LEQ arith_expr { RExpr($1, BLeq, $3) }
	| bool_expr AND bool_expr { BExpr($1, AND, $3) }
	| bool_expr OR bool_expr {BExpr($1, OR, $3) }
	

varvalue:
		ID  { VId($1) }
	| LITERAL          { VLiteral($1) }
	| FLOAT            { VFloat($1) }
		

arith_expr:
		ID  { VId($1) }
	| LITERAL          { VLiteral($1) }
	| FLOAT            { VFloat($1) }
	| arith_expr PLUS   arith_expr { VBinop($1, VAdd,   $3) }
  | arith_expr MINUS  arith_expr { VBinop($1, VSub,   $3) }
  | arith_expr TIMES  arith_expr { VBinop($1, VMult,  $3) }
  | arith_expr DIVIDE arith_expr { VBinop($1, VDiv,   $3) }
   	

expr:
    LITERAL          { Literal($1) }
	| STRINGLIT        { String($1) }
	| FLOAT            { Float($1) }
  | ID               { Id($1) }
  | arith_expr PLUS  arith_expr { Binop($1, Add,   $3) }
  | arith_expr MINUS  arith_expr { Binop($1, Sub,   $3) }
  | arith_expr TIMES  arith_expr { Binop($1, Mult,  $3) }
  | arith_expr DIVIDE arith_expr { Binop($1, Div,   $3) }
	| bool_expr        { Boolexpr($1) }
  /*| arith_expr EQ     arith_expr { Binop($1, Equal, $3) }
  | arith_expr NEQ    arith_expr { Binop($1, Neq,   $3) }
  | arith_expr LT     arith_expr { Binop($1, Less,  $3) }
  | arith_expr LEQ    arith_expr { Binop($1, Leq,   $3) }
  | arith_expr GT     arith_expr { Binop($1, Greater,  $3) }
  | arith_expr GEQ    arith_expr { Binop($1, Geq,   $3) }*/
	| ID ASSIGN expr   { Assign($1, $3) }
  | ID LPAREN actuals_opt RPAREN { Call($1, $3) }
  | LPAREN expr RPAREN { $2 }

/*

arith_expr:
		ID  { Id($1) }
	| LITERAL          { Literal($1) }
	| FLOAT            { Float($1) }
	| arith_expr PLUS   arith_expr { Binop($1, Add,   $3) }
  | arith_expr MINUS  arith_expr { Binop($1, Sub,   $3) }
  | arith_expr TIMES  arith_expr { Binop($1, Mult,  $3) }
  | arith_expr DIVIDE arith_expr { Binop($1, Div,   $3) }
   	

expr:
    LITERAL          { Literal($1) }
	| STRINGLIT        { String($1) }
	| FLOAT            { Float($1) }
	| TRUE             { Boolean(bool_of_string "true") }
	| FALSE            { Boolean(bool_of_string "false") }
  | ID               { Id($1) }
  | arith_expr PLUS  arith_expr { Binop($1, Add,   $3) }
  | arith_expr MINUS  arith_expr { Binop($1, Sub,   $3) }
  | arith_expr TIMES  arith_expr { Binop($1, Mult,  $3) }
  | arith_expr DIVIDE arith_expr { Binop($1, Div,   $3) }
  | arith_expr EQ     arith_expr { Binop($1, Equal, $3) }
  | arith_expr NEQ    arith_expr { Binop($1, Neq,   $3) }
  | arith_expr LT     arith_expr { Binop($1, Less,  $3) }
  | arith_expr LEQ    arith_expr { Binop($1, Leq,   $3) }
  | arith_expr GT     arith_expr { Binop($1, Greater,  $3) }
  | arith_expr GEQ    arith_expr { Binop($1, Geq,   $3) }
	| ID ASSIGN expr   { Assign($1, $3) }
  | ID LPAREN actuals_opt RPAREN { Call($1, $3) }
  | LPAREN expr RPAREN { $2 }

*/
actuals_opt:
    /* nothing */ { [] }
  | actuals_list  { List.rev $1 }

actuals_list:
    expr                    { [$1] }
  | actuals_list COMMA expr { $3 :: $1 }
